package com.onion.orm.dao.mybatis.interceptor;

import com.alibaba.druid.pool.DruidPooledPreparedStatement;
import com.mysql.cj.jdbc.ClientPreparedStatement;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.jdbc.PreparedStatementLogger;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import java.sql.Statement;


@Intercepts({
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class})
})
public class ThresholdInterceptor implements Interceptor {

    private Logger logger = LoggerFactory.getLogger(ThresholdInterceptor.class);

    private long sqlSlowThreshold = 1000L;
    private static final String SQL_SLOW_THRESHOLD_KEY = "sql.slow.threshold";

    @PostConstruct
    public void interceptor(){
        String slowThredshold = System.getProperty(SQL_SLOW_THRESHOLD_KEY);
        if(slowThredshold == null || StringUtils.isBlank(slowThredshold)){
            return;
        }else{
            this.sqlSlowThreshold = Long.parseLong(slowThredshold.trim());
        }
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = invocation.proceed();
        try {
            if (System.currentTimeMillis() - startTime >= sqlSlowThreshold) {
                Object[] args = invocation.getArgs();
                if (args.length >= 1 && args[0] instanceof Statement) {
                    Statement statement = (Statement) args[0];
                    //通过反射得到目标对象
                    MetaObject metaObjectStat = SystemMetaObject.forObject(statement);
                    String property = metaObjectStat.findProperty("h", false);
                    if("h".equals(property)){
                        //拿到代理对象实例
                        PreparedStatementLogger statementLogger = (PreparedStatementLogger) metaObjectStat.getValue("h");
                        Statement preparedStatement = statementLogger.getPreparedStatement();
                        logger.warn("慢SQL监控警告：costTime={}ms, sql={}", System.currentTimeMillis() - startTime, preparedStatement.toString());
                    }else{
                        if(metaObjectStat.getOriginalObject() instanceof DruidPooledPreparedStatement){
                            logger.warn("慢SQL监控警告：costTime={}ms, sql={}", System.currentTimeMillis() - startTime,
                                        ((DruidPooledPreparedStatement) metaObjectStat.getOriginalObject()).getSql());
                        }else{
                            logger.warn("慢SQL监控异常：不支持的对象类型={}", metaObjectStat.getOriginalObject().getClass());
                        }
                    }
                }
            }
        }catch (Throwable th){
            logger.warn("Mybatis SQL监控出错!", th);
        }finally {
            return result;
        }
    }

}